package orm

import (
	"fmt"
	"reflect"
)

type TableToSetAdapter struct {
	t TableDefiner
}

func (a TableToSetAdapter) QueryBody() string {
	return "`"+a.t.TableName()+"`"
}

func (TableToSetAdapter) CondAppendMode() CondAppendMode {
	return Where
}

func AdaptSetDefiner(meta interface{}) SetDefiner {
	if _meta, ok := meta.(SetDefiner); ok {
		return _meta
	} else if _meta, ok := meta.(TableDefiner); ok {
		return &TableToSetAdapter{t:_meta}
	} else {
		panic("未定义的元数据类型")
	}
}

type BaseModel struct {
	db DataBaseOperator
	meta interface{}
	cond string
	argv []interface{}
	order, limit string
}

func Base(db DataBaseOperator, meta interface{}) *BaseModel {
	return &BaseModel{
		db:   db,
		meta: meta,
		argv:make([]interface{}, 0),
	}
}

/**
 * 必须按照Where->Order的顺序调用，且只能调用一次，否则会发生错误
 */
func (m *BaseModel) Where(cond string, argv ...interface{}) *BaseModel {
	m.cond = cond
	m.argv = argv
	return m
}

func (m *BaseModel) Order(str string) *BaseModel {
	m.order = str
	return m
}

func (m *BaseModel) Limit(offset, length int) *BaseModel {
	m.limit = fmt.Sprintf("%d, %d", offset, length)
	return m
}

func (m *BaseModel) Select() (interface{}, error) {
	st := reflect.SliceOf(reflect.TypeOf(m.meta))
	dv := reflect.New(st).Elem()
	dv.Set(reflect.MakeSlice(st, 0, 0))
	err := selectTo(m.db, m.meta, dv.Addr().Interface(), m.cond, m.order, m.limit, m.argv...)
	return dv.Interface(), err
}

func (m *BaseModel) SelectTo(data interface{}) error {
	return selectTo(m.db, m.meta, data, m.cond, m.order, m.limit, m.argv...)
}

func (m *BaseModel) Count() (int, error) {
	return count(m.db, m.meta, m.cond, m.argv...)
}

func (m *BaseModel) Paging(p *PageInfo) (interface{}, error) {
	if p.No == 0 {panic("分页页码不能为零")}
	if p.Size == 0 {panic("分页条数不能为零")}
	data, err := m.Limit((p.No-1)*p.Size, p.Size).Select()
	if err != nil {
		return nil, err
	}
	count, err := m.Count()
	if err != nil {
		return nil, err
	}
	p.SetTotal(count)
	return data, nil
}

func (m *BaseModel) PagingTo(data interface{}, p *PageInfo) error {
	if p.No == 0 {panic("分页页码不能为零")}
	if p.Size == 0 {panic("分页条数不能为零")}
	err := m.Limit((p.No-1)*p.Size, p.Size).SelectTo(data)
	if err != nil {
		return err
	}
	count, err := m.Count()
	if err != nil {
		return err
	}
	p.SetTotal(count)
	return nil
}

// 返回sql语句，可以作为子查询
func (m *BaseModel) Sql(fields ...string) (string, []interface{}) {
	_fields := append(fields, "")[0]
	return build(m.meta, _fields, m.cond, m.order, m.limit, m.argv...)
}
